# -*- encoding: utf-8 -*-

# Copyright 2010 István Szentandrási
# <etude-music-player@googlegroups.com>

# This file is part of Etude.
#
# Etude is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as 
# published by the Free Software Foundation, either version 3 
# of the License, or (at your option) any later version.
#
# Etude is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Etude. If not, see <http://www.gnu.org/licenses/>.

import gobject
import time

from common import *

MAX_CACHE_SIZE = 15
MIN_CACHE_SIZE = 5

class BinData(gobject.GObject):

    """
    wrapper class for manipulating binary data on the server
    """

    def __init__(self, client):
        self.__gobject_init__()
        self.client = client
        self._cache = {}
        self._cache_updated = {}

    def add(self, data, cb):
        """
        store the data on the server, cb will get a hash as a parameter, which can
        be used to retrieve the data later with the retrieve() function
        """
        #cb(hash)
        def _add(result):
            if result.is_error():
                cb(None)
            else:
                cb(result.value())
                self._cache[hash] = data
                self._cache_updated[hash] = time.time()
                self.__clean_cache()
            return False
        if not hasattr(cb, '__call__'):
            return
        self.client.xmms_slow.bindata_add(data, _add)

    @check_fast(False)
    @check_wait(False)
    @check_cb_None()
    def list(self, cb = None):
        """
        list the stored data on the server
        """
        def _list(result):
            cb(result.value())
        if not hasattr(cb, '__call__'):
            return None
        return self.client.xmms.bindata_list(_list)
    
    @check_cb_None()
    def remove(self, hash, cb = None):
        """
        remove stored data defined by hash
        """
        self.client.xmms_slow.bindata_remove(hash, cb)
        try:
            del self._cache[hash]
            del self._cache_updated[hash]
        except KeyError:
            pass
    
    def __clean_cache(self):
        
        cache_size = len(self._cache)
        
        if cache_size != len(self._cache_updated):
            print("Warning: somewhere caching went wrong.")
            not_in_upd = [hash for hash in self._cache.keys() if hash not in self._cache_updated]
            not_in_cache = [hash for hash in self._cache_updated.keys() if hash not in self._cache]
            for hash in not_in_upd:
                self._cache_updated[hash] = time.time()
            for hash in not_in_cache:
                del self._cache_updated[hash]

        if len(self._cache) > MAX_CACHE_SIZE:
            sort_cache = sorted(self._cache_updated, lambda x: x[1])
            delete_hashes = [hash for hash,time in sort_cache[:cache_size - MIN_CACHE_SIZE]]
            for hash in delete_hashes:
                del self._cache[hash]
                del self._cache_updated[hash]


    @check_fast(False)
    def retrieve(self, hash, cb, cb_args = {}):
        """
        retrieve binary data from the server
        """
        def _retrieve(result):
            if result.is_error():
                cb(None, **cb_args)
            else:
                cb(result.value(), **cb_args)
                self._cache[hash] = result.value()
                self._cache_updated[hash] = time.time()
                self.__clean_cache()
            return False
        if not hasattr(cb, '__call__'):
            return
        if hash in self._cache:
            cb(self._cache[hash], **cb_args)
            self._cache_updated[hash] = time.time()
            return
        self.client.xmms.bindata_retrieve(hash, _retrieve)

